﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common;

namespace Betfairer
{
    //_betfairGlobal.getActiveEventTypes(eventsReq); - ?

    /// <summary>
    /// Class for the betfair.com API interaction.
    /// </summary>
    public class Global
    {
        private static Global instance;
        private BetfairGlobal.BFGlobalServiceClient _betfairGlobal;
        private BetfairUk.BFExchangeServiceClient _betfairUk;
        private BetfairGlobal.APIRequestHeader _headerGlobal;
        private string _currency;

        public static Global Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new Global();
                }

                return instance;
            }
        }

        private Global()
        {
            _betfairGlobal = new BetfairGlobal.BFGlobalServiceClient();
            _betfairUk = new BetfairUk.BFExchangeServiceClient();
            _headerGlobal = new BetfairGlobal.APIRequestHeader();
        }

        public string Currency
        {
            get
            {
                return _currency;
            }
        }

        private BetfairGlobal.APIRequestHeader GetHeaderGlobal()
        {
            BetfairGlobal.APIRequestHeader header = new BetfairGlobal.APIRequestHeader();
            header.sessionToken = _headerGlobal.sessionToken;

            return header;
        }

        private BetfairUk.APIRequestHeader GetHeaderUk()
        {
            BetfairUk.APIRequestHeader header = new BetfairUk.APIRequestHeader();
            header.sessionToken = _headerGlobal.sessionToken;

            return header;
        }

        private void CheckHeader(BetfairGlobal.APIResponseHeader header)
        {
            _headerGlobal.sessionToken = header.sessionToken;
        }

        private void CheckHeader(BetfairUk.APIResponseHeader header)
        {
            _headerGlobal.sessionToken = header.sessionToken;
        }

        public string Login(string username, string password)
        {
            BetfairGlobal.LoginReq loginReq = new BetfairGlobal.LoginReq();
            BetfairGlobal.LoginResp loginRes = new BetfairGlobal.LoginResp();

            loginReq.username = username;
            loginReq.password = password;
            loginReq.productId = 82;

            Console.WriteLine("[BETFAIRER] Logging in to batfair.com...");
            loginRes = _betfairGlobal.login(loginReq);
            CheckHeader(loginRes.header);

            return String.Format("Login: {0}{1}Header: {2}",
                loginRes.errorCode,
                Environment.NewLine,
                loginRes.header.errorCode);
        }

        public string GetProfile()
        {
            BetfairGlobal.ViewProfileReq profileReq = new BetfairGlobal.ViewProfileReq();
            BetfairGlobal.ViewProfileResp profileResp = new BetfairGlobal.ViewProfileResp();
            profileReq.header = _headerGlobal;
            profileResp = _betfairGlobal.viewProfile(profileReq);
            CheckHeader(profileResp.header);
            _currency = profileResp.currency;

            return profileResp.userName;
        }

        public string GetAccountFunds()
        {
            BetfairUk.GetAccountFundsReq oAccountFundsReq = new BetfairUk.GetAccountFundsReq();
            BetfairUk.GetAccountFundsResp oAccountFundsRes = new BetfairUk.GetAccountFundsResp();
            oAccountFundsReq.header = GetHeaderUk();
            oAccountFundsRes = _betfairUk.getAccountFunds(oAccountFundsReq);
            CheckHeader(oAccountFundsRes.header);
            return String.Format("{0} {1}", _currency, oAccountFundsRes.availBalance);
        }

        public List<SportEvent> GetSportEvents()
        {
            List<SportEvent> result = new List<SportEvent>();

            BetfairGlobal.GetEventTypesReq eventsReq = new BetfairGlobal.GetEventTypesReq();
            BetfairGlobal.GetEventTypesResp eventsResp = new BetfairGlobal.GetEventTypesResp();
            eventsReq.header = _headerGlobal;
            //eventsResp = _betfairGlobal.getActiveEventTypes(eventsReq);
            eventsResp = _betfairGlobal.getAllEventTypes(eventsReq);

            CheckHeader(eventsResp.header);

            if (eventsResp.errorCode == BetfairGlobal.GetEventsErrorEnum.OK)
            {
                for (int i = 0; i < eventsResp.eventTypeItems.Length; ++i)
                {
                    result.Add(new SportEvent
                    {
                        Id = eventsResp.eventTypeItems[i].id,
                        Title = eventsResp.eventTypeItems[i].name
                    });
                }
            }
            else
            {
                Console.WriteLine("Error: " + eventsResp.errorCode);
            }

            return result;
        }

        public List<Market> GetMarkets(int sportEventId, DateTime formDate, DateTime toDate)
        {
            BetfairUk.GetAllMarketsReq marketsReq = new BetfairUk.GetAllMarketsReq();
            BetfairUk.GetAllMarketsResp marketsResp = new BetfairUk.GetAllMarketsResp();
            marketsReq.header = GetHeaderUk();
            marketsReq.eventTypeIds = new int?[1];
            marketsReq.eventTypeIds[0] = sportEventId;
            marketsReq.fromDate = formDate;// DateTime.Today;
            marketsReq.toDate = toDate; // DateTime.Today.AddDays(10);
            marketsReq.locale = "UK";

            //Console.WriteLine(String.Format("[BETFAIRER] Retrieving markets for sport event with id {0}...", sportEventId));
            marketsResp = _betfairUk.getAllMarkets(marketsReq);
            CheckHeader(marketsResp.header);

            if (marketsResp.errorCode == BetfairUk.GetAllMarketsErrorEnum.OK)
            {
                return new MarketHelper().ParseMarket(marketsResp.marketData);
            }
            else
            {
                Console.WriteLine("Error: " + marketsResp.errorCode);
                return null;
            }
        }

        public List<Pair<int, string>> GetRunnersMetadata(int marketId)
        {
            BetfairUk.GetMarketReq getMarketReq = new BetfairUk.GetMarketReq();
            BetfairUk.GetMarketResp getMarketResp = new BetfairUk.GetMarketResp();

            getMarketReq.header = GetHeaderUk();
            getMarketReq.marketId = marketId;
            getMarketResp = _betfairUk.getMarket(getMarketReq);

            CheckHeader(getMarketResp.header);
            List<Pair<int, string>> result = new List<Pair<int, string>>();
            if (getMarketResp.errorCode == BetfairUk.GetMarketErrorEnum.OK)
            {
                BetfairUk.Runner[] runners = getMarketResp.market.runners;

                for (int i = 0; i < runners.Length; ++i)
                {
                    result.Add(new Pair<int, string>(runners[i].selectionId, runners[i].name));
                }
            }
            else
            {
                Console.WriteLine("GetRunnersNames() error: " + getMarketResp.header.errorCode);
            }

            return result;
        }

        //public BetfairUk.MarketPrices GetOdds(int marketId)
        //{
        //    BetfairUk.GetMarketPricesReq marketPricesReq = new BetfairUk.GetMarketPricesReq();
        //    BetfairUk.GetMarketPricesResp marketPricesResp = new BetfairUk.GetMarketPricesResp();

        //    marketPricesReq.header = GetHeaderUk();
        //    marketPricesReq.marketId = marketId;

        //    //Console.WriteLine(String.Format("[BETFAIRER] Retrieving odds for market with id {0}...", marketId));
        //    marketPricesResp = _betfairUk.getMarketPrices(marketPricesReq);

        //    CheckHeader(marketPricesResp.header);

        //    if (marketPricesResp.errorCode.Equals(BetfairUk.GetMarketPricesErrorEnum.OK))
        //    {
        //        BetfairUk.MarketPrices mps = marketPricesResp.marketPrices;

        //        return mps;
        //    }
        //    else
        //    {
        //        Console.WriteLine("GetOdds() error: " + marketPricesResp.errorCode + " " + marketPricesResp.header.errorCode);

        //        return null;
        //    }
        //}

        /// <summary>
        /// The method allows you to retrieve dynamic market data for a given Market ID in a compressed format.
        /// </summary>
        /// <param name="marketId"></param>
        /// <returns></returns>
        public String GetMarketPricesCompressed(Int32 marketId)
        {
            BetfairUk.GetMarketPricesCompressedReq marketPricesReq
                = new BetfairUk.GetMarketPricesCompressedReq();
            BetfairUk.GetMarketPricesCompressedResp marketPricesResp
                = new BetfairUk.GetMarketPricesCompressedResp();

            marketPricesReq.header = GetHeaderUk();
            marketPricesReq.marketId = marketId;

            //Console.WriteLine(String.Format("[BETFAIRER] Retrieving complete prices for market with id {0}...", marketId));
            marketPricesResp = _betfairUk.getMarketPricesCompressed(marketPricesReq);

            CheckHeader(marketPricesResp.header);

            if (marketPricesResp.errorCode.Equals(BetfairUk.GetMarketPricesErrorEnum.OK))
            {
                return marketPricesResp.marketPrices;
            }
            else
            {
                Console.WriteLine("GetMarketPricesCompressed() error: "
                    + marketPricesResp.errorCode + " " + marketPricesResp.header.errorCode);

                return String.Empty;
            }
        }

        //public String GetCompleteMarketPricesCompressed(int marketId)
        //{
        //    BetfairUk.GetCompleteMarketPricesCompressedReq completeMarketPricesReq 
        //        = new BetfairUk.GetCompleteMarketPricesCompressedReq();
        //    BetfairUk.GetCompleteMarketPricesCompressedResp completeMarketPricesResp 
        //        = new BetfairUk.GetCompleteMarketPricesCompressedResp();

        //    completeMarketPricesReq.header = GetHeaderUk();
        //    completeMarketPricesReq.marketId = marketId;

        //    //Console.WriteLine(String.Format("[BETFAIRER] Retrieving complete prices for market with id {0}...", marketId));
        //    completeMarketPricesResp = _betfairUk.getCompleteMarketPricesCompressed(completeMarketPricesReq);

        //    CheckHeader(completeMarketPricesResp.header);

        //    if (completeMarketPricesResp.errorCode.Equals(BetfairUk.GetCompleteMarketPricesErrorEnum.OK))
        //    {
        //        return completeMarketPricesResp.completeMarketPrices;
        //    }
        //    else
        //    {
        //        Console.WriteLine("GetCompleteMarketPricesCompressed() error: " 
        //            + completeMarketPricesResp.errorCode + " " + completeMarketPricesResp.header.errorCode);

        //        return String.Empty;
        //    }
        //}

        public List<long> PlaceBets(int marketId, List<Bet> bets)
        {
            BetfairUk.PlaceBetsReq placeBetsRequest = new BetfairUk.PlaceBetsReq();
            placeBetsRequest.header = GetHeaderUk();
            placeBetsRequest.bets = new BetfairUk.PlaceBets[bets.Count];
            int counter = 0;

            foreach (Bet bet in bets)
            {
                BetfairUk.PlaceBets temp = new BetfairUk.PlaceBets();
                temp.asianLineId = 0;
                temp.betCategoryType = BetfairUk.BetCategoryTypeEnum.E;
                temp.betPersistenceType = BetfairUk.BetPersistenceTypeEnum.NONE;
                temp.betType = bet.Type == BetType.BACK ? BetfairUk.BetTypeEnum.B : BetfairUk.BetTypeEnum.L; // back or lay
                temp.marketId = marketId;
                temp.price = bet.Odd; // the price (odds) you want to set for the bet
                temp.selectionId = bet.Selection; // id of the desired runner or selection within the market
                temp.size = bet.Money; // the stake (amount) for an exchange bet
                temp.bspLiability = 0; // must be 0 for non-SP bet

                placeBetsRequest.bets[counter++] = temp;
            }

            BetfairUk.PlaceBetsResp placeBetsResponse = _betfairUk.placeBets(placeBetsRequest);

            CheckHeader(placeBetsResponse.header);

            List<long> ids = new List<long>();
            if (placeBetsResponse.errorCode.Equals(BetfairUk.PlaceBetsErrorEnum.OK))
            {
                for (int i = 0; i < placeBetsResponse.betResults.Length; ++i)
                {
                    Console.WriteLine(String.Format("Result code: {0}.", placeBetsResponse.betResults[i].resultCode));

                    if (placeBetsResponse.betResults[i].success)
                    {
                        Console.WriteLine(String.Format("Bet id: {0}.", placeBetsResponse.betResults[i].betId));
                        ids.Add(placeBetsResponse.betResults[i].betId);
                    }
                    else
                    {
                        ids.Add(Int64.MinValue);
                    }
                }
            }
            else
            {
                Console.WriteLine(
                    String.Format("Error code: {0}. Header error code: {1}."
                    , placeBetsResponse.errorCode
                    , placeBetsResponse.header.errorCode));
            }

            return ids;
        }

        public BetfairGlobal.EventType[] GetEventTypes()
        {
            BetfairGlobal.GetEventTypesReq getEventTypesReq = new BetfairGlobal.GetEventTypesReq();
            BetfairGlobal.GetEventTypesResp getEventTypesResp = new BetfairGlobal.GetEventTypesResp();

            getEventTypesReq.header = GetHeaderGlobal();
            getEventTypesResp = _betfairGlobal.getAllEventTypes(getEventTypesReq);

            if (getEventTypesResp.errorCode.Equals(BetfairGlobal.GetEventsErrorEnum.OK))
            {
                return getEventTypesResp.eventTypeItems;
            }
            else
            {
                Console.WriteLine("GetEventTypes() error: "
                    + getEventTypesResp.errorCode + " " + getEventTypesResp.header.errorCode);

                return new BetfairGlobal.EventType[0];
            }
        }

        //public void PlaceBet(int marketId, int selection, BetfairUk.BetTypeEnum betType, double betPrice, double betSize)
        //{
        //    BetfairUk.PlaceBets bet = new BetfairUk.PlaceBets();
        //    bet.asianLineId = 0;
        //    bet.betCategoryType = BetfairUk.BetCategoryTypeEnum.E;
        //    bet.betPersistenceType = BetfairUk.BetPersistenceTypeEnum.NONE;
        //    bet.betType = betType; // back or lay
        //    bet.marketId = marketId;
        //    bet.price = betPrice; // the price (odds) you want to set for the bet
        //    bet.selectionId = selection; // id of the desired runner or selection within the market
        //    bet.size = betSize; // the stake (amount) for an exchange bet
        //    bet.bspLiability = 0; // must be 0 for non-SP bet

        //    BetfairUk.PlaceBetsReq placeBetsRequest = new BetfairUk.PlaceBetsReq();
        //    placeBetsRequest.bets[0] = bet;
        //    placeBetsRequest.header = GetHeaderUk();

        //    BetfairUk.PlaceBetsResp placeBetsResponse = _betfairUk.placeBets(placeBetsRequest);

        //    CheckHeader(placeBetsResponse.header);

        //    List<long> ids = new List<long>();
        //    if (placeBetsResponse.errorCode.Equals(BetfairUk.PlaceBetsErrorEnum.OK))
        //    {
        //        for (int i = 0; i < placeBetsResponse.betResults.Length; ++i)
        //        {
        //            Console.WriteLine(String.Format("Result code: {0}.", placeBetsResponse.betResults[i].resultCode));

        //            if (placeBetsResponse.betResults[i].success)
        //            {
        //                Console.WriteLine(String.Format("Bet id: {0}.", placeBetsResponse.betResults[i].betId));
        //                ids.Add(placeBetsResponse.betResults[i].betId);
        //            }
        //            else
        //            {
        //                ids.Add(Int64.MinValue);
        //            }
        //        }
        //    }
        //    else
        //    {
        //        Console.WriteLine(
        //            String.Format("Error code: {0}. Header error code: {1}."
        //            , placeBetsResponse.errorCode
        //            , placeBetsResponse.header.errorCode));
        //    }


        //}


        // http://forum.bdp.betfair.com/showthread.php?t=112&page=19
        //        Private Sub PlaceBet(ByVal YourMarket As Integer, ByVal YourSelection As Integer, ByVal YourBetType As BFUK.BetTypeEnum, ByVal BetPrice As Decimal, ByVal BetSize As Decimal)

        //  Dim oPlaceBetsReq As New BFUK.PlaceBetsReq      'Create the request object
        //  Dim oPlaceBetsResp As BFUK.PlaceBetsResp       'A variable to hold the response object
        //  Dim oBet As New BFUK.PlaceBets              'An object to specify the bet
        //  With oPlaceBetsReq
        //    .header = oHeaderUK()            'The standard header
        //    With oBet                        'Specify the bet details:
        //      .asianLineId = 0               'Non-asian handicap market 
        //      .betCategoryType = BFUK.BetCategoryTypeEnum.E    'Normal exchange bet
        //      .betPersistenceType = BFUK.BetPersistenceTypeEnum.NONE  'Standard bet (not SP or InPlay persistence)
        //      .betType = YourBetType         'Back or Lay
        //      .marketId = YourMarket         'The market of interest
        //      .price = BetPrice              'The desired price (odds)
        //      .selectionId = YourSelection   'Your selection of interest
        //      .size = BetSize                'The bet amount (stake)
        //      .bspLiability = 0              'Must be 0 for non-SP bet
        //    End With
        //    ReDim .bets(0)    'Array length = 1 to hold single bet
        //    .bets(0) = oBet   'Put the bet details object in element 0
        //  End With

        //  oPlaceBetsResp = BetFairUK.placeBets(oPlaceBetsReq)  'Call API to place the bet

        //  With oPlaceBetsResp    'The response
        //    CheckHeader(.header)
        //    Print("ErrorCode = " & .errorCode.ToString)
        //    If .errorCode = BFUK.PlaceBetsErrorEnum.OK Then  'The call succeeded
        //      For i = 0 To .betResults.Length - 1   'In this case there should only be 1 result.
        //        With .betResults(i)
        //          Print(.resultCode.ToString)   'Result of the bet placement
        //          If .success Then 
        //            Print("BetId = " & .betId)  'Print the betId if successful
        //            TheBetId = .betId           'Save this
        //           End If
        //        End With
        //      Next
        //    End If
        //  End With

        //End Sub
    }
}
